//
//  EXNSAdditions.m
//  MagicBase
//
//  Created by PH on 19.1.10.
//  Copyright 2010 Codesign. All rights reserved.
//

#import "EXNSAdditions.h"

static const char encodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

@implementation NSNumber (MBAdditions)

+ (NSNumber*)numberWithString:(NSString*)text {
	text = [text stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
	if ([text rangeOfString: @"."].location != NSNotFound) {
		return [NSNumber numberWithDouble: [text doubleValue]];
	} else //if ([text characterAtIndex: 0] == '-') {
		return [NSNumber numberWithInteger: [text integerValue]];
	//} else return [NSNumber numberWithUnsignedInteger: [text integerValue]];
}

- (BOOL)isReal {
	const char* cType = [self objCType];
	return *cType == 'f' || *cType == 'd';
}

@end

@implementation NSData (Base64Additions)

+ (NSData*)dataWithBase64String:(NSString*)string {
	if (string == nil)
		[NSException raise: NSInvalidArgumentException format: @""];
	if ([string length] == 0)
		return [NSData data];
	
	static char *decodingTable = NULL;
	if (decodingTable == NULL)
	{
		decodingTable = malloc(256);
		if (decodingTable == NULL)
			return nil;
		memset(decodingTable, CHAR_MAX, 256);
		NSUInteger i;
		for (i = 0; i < 64; i++)
			decodingTable[(short)encodingTable[i]] = i;
	}
	
	const char *characters = [string cStringUsingEncoding:NSASCIIStringEncoding];
	if (characters == NULL)     //  Not an ASCII string!
		return nil;
	char *bytes = malloc((([string length] + 3) / 4) * 3);
	if (bytes == NULL)
		return nil;
	NSUInteger length = 0;
	
	NSUInteger i = 0;
	while (YES)
	{
		char buffer[4];
		short bufferLength;
		for (bufferLength = 0; bufferLength < 4; i++)
		{
			if (characters[i] == '\0')
				break;
			if (isspace(characters[i]) || characters[i] == '=')
				continue;
			buffer[bufferLength] = decodingTable[(short)characters[i]];
			if (buffer[bufferLength++] == CHAR_MAX)      //  Illegal character!
			{
				free(bytes);
				return nil;
			}
		}
		
		if (bufferLength == 0)
			break;
		if (bufferLength == 1)      //  At least two characters are needed to produce one byte!
		{
			free(bytes);
			return nil;
		}
		
		//  Decode the characters in the buffer to bytes.
		bytes[length++] = (buffer[0] << 2) | (buffer[1] >> 4);
		if (bufferLength > 2)
			bytes[length++] = (buffer[1] << 4) | (buffer[2] >> 2);
		if (bufferLength > 3)
			bytes[length++] = (buffer[2] << 6) | buffer[3];
	}
	
	realloc(bytes, length);
	return [NSData dataWithBytesNoCopy:bytes length:length];
}

- (NSString*)base64String {
	if ([self length] == 0)
		return @"";
	
    char *characters = malloc((([self length] + 2) / 3) * 4);
	if (characters == NULL)
		return nil;
	NSUInteger length = 0;
	
	NSUInteger i = 0;
	while (i < [self length])
	{
		char buffer[3] = {0,0,0};
		short bufferLength = 0;
		while (bufferLength < 3 && i < [self length])
			buffer[bufferLength++] = ((char *)[self bytes])[i++];
		
		//  Encode the bytes in the buffer to four characters, including padding "=" characters if necessary.
		characters[length++] = encodingTable[(buffer[0] & 0xFC) >> 2];
		characters[length++] = encodingTable[((buffer[0] & 0x03) << 4) | ((buffer[1] & 0xF0) >> 4)];
		if (bufferLength > 1)
			characters[length++] = encodingTable[((buffer[1] & 0x0F) << 2) | ((buffer[2] & 0xC0) >> 6)];
		else characters[length++] = '=';
		if (bufferLength > 2)
			characters[length++] = encodingTable[buffer[2] & 0x3F];
		else characters[length++] = '=';	
	}
	
	return [[[NSString alloc] initWithBytesNoCopy:characters length:length encoding:NSASCIIStringEncoding freeWhenDone:YES] autorelease];
}

/*- (NSString*)base64String {
	const char* chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$*";
	NSMutableString* result = [NSMutableString stringWithFormat: @"%10d", [self length]];
	for (int i = 0; i < [self length]; i += 3) {
		int byte1 = ((unsigned char*) [self bytes])[i];
		int byte2 = i < [self length] - 1 ? ((unsigned char*) [self bytes])[i + 1] : 0;
		int byte3 = i < [self length] - 2 ? ((unsigned char*) [self bytes])[i + 2] : 0;
		//NSLog(@"%d %d %d", byte1, byte2, byte3);
		int index1 = byte1 & 63;
		int index2 = ((byte1 >> 6) & 63) | ((byte2 << 2) & 63);
		int index3 = ((byte2 >> 4) & 63) | ((byte3 << 4) & 63);
		int index4 = (byte3 >> 2) & 63;
		//NSAssert(index1 >= 0 && index1 < 64, @"Index1 out of bounds");
		//NSAssert(index2 >= 0 && index2 < 64, @"Index2 out of bounds");
		//NSAssert(index3 >= 0 && index3 < 64, @"Index3 out of bounds");
		//NSAssert(index4 >= 0 && index4 < 64, @"Index4 out of bounds");
		char char1 = chars[index1];
		char char2 = chars[index2];
		char char3 = chars[index3];
		char char4 = chars[index4];
		[result appendFormat: @"%c%c%c%c", char1, char2, char3, char4];
	}
	return result;
}

+ (NSData*)dataWithBase64String:(NSString*)encodedString {
	const char* chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$*";
	NSUInteger length = [[encodedString substringToIndex: 10] integerValue];
	encodedString = [[encodedString substringFromIndex: 10] stringByReplacingOccurrencesOfString: @"\n" withString: @""];
	encodedString = [encodedString stringByReplacingOccurrencesOfString: @"\r" withString: @""];
	const char* cString = [encodedString cStringUsingEncoding: NSUTF8StringEncoding];
	NSUInteger cslen = strlen(cString), index = 0;
	char* array = malloc(length);
	for (int i = 0; i < cslen; i += 4) {
		//NSAssert(strchr(chars, cString[i]) != NULL, @"Char1 not found");
		//NSAssert(strchr(chars, cString[i + 1]) != NULL, @"Char2 not found");
		//NSAssert(strchr(chars, cString[i + 2]) != NULL, @"Char3 not found");
		//NSAssert(strchr(chars, cString[i + 3]) != NULL, @"Char4 not found");
		int char1 = strchr(chars, cString[i]) - chars;
		int char2 = strchr(chars, cString[i + 1]) - chars;
		int char3 = strchr(chars, cString[i + 2]) - chars;
		int char4 = strchr(chars, cString[i + 3]) - chars;
		array[index++] = char1 | (char2 << 6);
		array[index++] = (char2 >> 2) | (char3 << 4);
		array[index++] = ((char3 >> 4) & 63) | (char4 << 2);
	}
	NSData* result = [NSData dataWithBytes: array length: length];
	free(array);
	return result;
}*/

@end

@implementation NSString (Base64Additions)

- (BOOL)isNumber {
	NSString* value = self;
	if ([value length] > 0) {
		if ([value characterAtIndex: 0] == '-' && [value length] >= 2) value = [value substringFromIndex: 1];
		value = [value stringByReplacingOccurrencesOfString: @"," withString: @"."];
		NSArray* comps = [value componentsSeparatedByString: @"."];
		if ([comps count] <= 2) {
			NSCharacterSet* numChars = [NSCharacterSet decimalDigitCharacterSet];
			for (NSString* comp in comps) {
				for (int i = 0; i < [comp length]; i++) {
					if ([numChars characterIsMember: [comp characterAtIndex: i]] == NO) return NO;
				}
			}
			return YES;
		}
	}
	return NO;
}

- (NSString*)base64String {
	return [[self dataUsingEncoding: NSUTF8StringEncoding] base64String];
}

+ (NSString*)stringWithBase64String:(NSString*)encodedString {
	NSData* data = [NSData dataWithBase64String: encodedString];
	return [[[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding] autorelease];
}

@end
